from utils.single import SingletonMeta
import grpc
from services.protos import user_pb2, user_pb2_grpc
from .decorators import grpc_error_handler, grpc_retry_handler
from utils.tll_consul import ConsulClient
from loguru import logger
from typing import Tuple, Optional
import asyncio

consul=ConsulClient()

class UserStub:
    def __init__(self, max_connection_attempts: int = 2):
        self.max_connection_attempts = max_connection_attempts
        self.channel: Optional[grpc.aio.Channel] = None
        self.stub: Optional[user_pb2_grpc.UserStub] = None
        
    @property
    async def user_service_addr(self) -> str:
        """获取用户服务地址，支持重试机制"""
        for attempt in range(self.max_connection_attempts):
            try:
                address, port = consul.get_one_user_service_address()
                
                if address is None:
                    logger.warning(f"第 {attempt + 1} 次尝试获取服务地址失败，尝试刷新服务发现...")
                    
                    # 刷新服务发现
                    await consul.fetch_user_service_addresses(only_healthy=True)
                    
                    if attempt < self.max_connection_attempts - 1:
                        await asyncio.sleep(1.0 * (attempt + 1))  # 递增延迟
                        continue
                    else:
                        raise RuntimeError("无法获取用户服务地址，请检查consul连接和服务注册")
                else:
                    service_addr = f"{address}:{port}"
                    logger.info(f"用户服务地址: {service_addr}")
                    return service_addr
                    
            except Exception as e:
                logger.error(f"获取服务地址异常 (尝试 {attempt + 1}/{self.max_connection_attempts}): {e}")
                if attempt < self.max_connection_attempts - 1:
                    await asyncio.sleep(1.0 * (attempt + 1))
                    continue
                else:
                    raise
        
        raise RuntimeError("所有尝试都失败，无法获取用户服务地址")
        
    async def __aenter__(self):
        service_addr = await self.user_service_addr
        self.channel = grpc.aio.insecure_channel(service_addr)
        self.stub = user_pb2_grpc.UserStub(self.channel)
        return self.stub
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.channel:
            await self.channel.close()
            self.channel = None
            self.stub = None



class UserServiceClient(metaclass=SingletonMeta): 
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def get_or_create_user_by_mobile(self,mobile:str):
        async with UserStub() as stub:
            request=user_pb2.MobileRequest(mobile=mobile)  # type: ignore
            response=await stub.GetOrCreateUserByMobile(request)
            return response.user_info
            
    @grpc_retry_handler(max_retries=2, retry_delay=0.5)
    async def update_username(self,user_id:int,username:str):
        async with UserStub() as stub:
            request=user_pb2.UsernameRequest(id=user_id,username=username)  # type: ignore
            await stub.UpdateUsername(request)
            
    @grpc_retry_handler(max_retries=2, retry_delay=0.5)
    async def update_password(self,user_id:int,password:str):
        async with UserStub() as stub:
            request=user_pb2.PasswordRequest(id=user_id,password=password)  # type: ignore
            await stub.UpdatePassword(request)
            
    @grpc_retry_handler(max_retries=2, retry_delay=0.5)
    async def update_avatar(self,user_id:int,avatar:str):
        async with UserStub() as stub:
            request=user_pb2.AvatarRequest(id=user_id,avatar=avatar)  # type: ignore
            await stub.UpdateAvatar(request)
            
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def get_user_by_id(self,user_id:int):
        async with UserStub() as stub:
            request=user_pb2.IdRequest(id=user_id)  # type: ignore
            response=await stub.GetUserById(request)
            return response.user_info
        
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def get_user_by_mobile(self,mobile:str):
        async with UserStub() as stub:
            request=user_pb2.MobileRequest(mobile=mobile)  # type: ignore
            response=await stub.GetUserByMobile(request)
            return response.user_info
        
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def get_user_list(self,page:int=1,page_size:int=10):
        async with UserStub() as stub:
            request=user_pb2.PageRequest(page=page,page_size=page_size)  # type: ignore
            response=await stub.GetUserList(request)
            return response
        
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def verify_user(self,mobile:str,password:str):
        async with UserStub() as stub:
            request=user_pb2.VerifyUserRequest(mobile=mobile,password=password)  # type: ignore
            response=await stub.VerifyUser(request)
            return response.user_info
        